/*
 * Copyright 2012 The Netty Project
 *
 * The Netty Project licenses this file to you under the Apache License,
 * version 2.0 (the "License"); you may not use this file except in compliance
 * with the License. You may obtain a copy of the License at:
 *
 *   http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
 * License for the specific language governing permissions and limitations
 * under the License.
 */
package com.gitee.kamismile.gateweb.component.handler;

import com.gitee.kamismile.gateweb.component.common.APPWSChannel;
import com.gitee.kamismile.gateweb.component.server.WebSocketConstant;
import com.gitee.kamismile.stone.commmon.util.JsonUtil;
import com.gitee.kamismile.stone.commmon.util.ValueUtils;
import com.google.gson.JsonSyntaxException;
import io.netty.channel.Channel;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelId;
import io.netty.channel.SimpleChannelInboundHandler;
import io.netty.handler.codec.http.websocketx.TextWebSocketFrame;
import io.netty.handler.codec.http.websocketx.WebSocketFrame;
import com.gitee.kamismile.gateweb.component.pojo.MessageRouter;
import org.apache.commons.lang3.exception.ExceptionUtils;
import org.apache.logging.log4j.LogManager;

import java.io.IOException;
import java.util.HashMap;
import java.util.Map;

/**
 * Echoes uppercase content of text frames.
 */
public class WebSocketFrameHandler extends SimpleChannelInboundHandler<WebSocketFrame> {

    protected final org.apache.logging.log4j.Logger logger = LogManager.getLogger(WebSocketFrameHandler.class);


    private WebSocketConstant webSocketConstant;

    private long seq;

    private String reqCmd;


    public WebSocketConstant getWebSocketConstant() {
        return webSocketConstant;
    }

    public void setWebSocketConstant(WebSocketConstant webSocketConstant) {
        this.webSocketConstant = webSocketConstant;
    }

    @Override
    public void channelActive(ChannelHandlerContext ctx) throws Exception {
        Channel channel = ctx.channel();
        ChannelId channelId = channel.id();
        APPWSChannel.channels.add(channel);
        super.channelActive(ctx);
    }

    @Override
    protected void channelRead0(ChannelHandlerContext ctx, WebSocketFrame frame) throws Exception {
        // ping and pong frames already handled
        if (frame instanceof TextWebSocketFrame) {
            // Send the uppercase string back.
            String request = ((TextWebSocketFrame) frame).text();
            MessageRouter mr = JsonUtil.fromJson(request, MessageRouter.class);

            String clientId = getClientIdString(ctx);
            String remoteAddress = ctx.channel().remoteAddress().toString();
            long startTime = System.currentTimeMillis();

            this.seq = mr.getSeq();
            Map<String, Object> dataJson = ValueUtils.isNull(mr.getSerializedData()) ? new HashMap<String, Object>() : JsonUtil.json2Map(mr.getSerializedData());
            dataJson.put("cmd", mr.getCmd());
            dataJson.put("seq", seq);
//            dataJson.put("traceId", snowflakeIdWorker.nextId());
            dataJson.put("clientId", clientId);
            dataJson.put("remoteAddress", remoteAddress);

            this.reqCmd = mr.getCmd();

            if (this.reqCmd.startsWith("url")) {
                this.reqCmd = "url";
            }

            if (this.reqCmd.contains("auth")) {
                Channel channel = ctx.channel();
                ChannelId channelId = channel.id();
                APPWSChannel.channelsText.put(channelId.asLongText(), channelId);
            }


            ctx.writeAndFlush(new TextWebSocketFrame(JsonUtil.toJson(mr)));
        } else {
            String message = "unsupported frame type: " + frame.getClass().getName();
            throw new UnsupportedOperationException(message);
        }
    }

    protected String getClientIdString(ChannelHandlerContext ctx) throws IOException {
        ChannelId channelId = ctx.channel().id();
        String clientId = channelId.asLongText();
        return clientId;
    }

    @Override
    public void channelReadComplete(ChannelHandlerContext ctx) {
        ctx.flush();
    }

    @Override
    public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) {
        MessageRouter mr = new MessageRouter();
        mr.setSeq(ValueUtils.isLongNull(this.seq, 0L));
        mr.setRouter(ValueUtils.isStringNull(webSocketConstant.getRouter()));
        mr.setCmd(ValueUtils.isStringNull(this.reqCmd));
        if (cause instanceof JsonSyntaxException) {
            mr.setCode(2);
            ctx.writeAndFlush(new TextWebSocketFrame(JsonUtil.toJson(mr)));
            return;
        }
        ChannelId client = APPWSChannel.channelsText.remove(ctx.channel().id().asLongText());
        APPWSChannel.channels.remove(client);
        ctx.close();
        logger.error(ExceptionUtils.getMessage(cause));
    }

    @Override
    public void channelUnregistered(ChannelHandlerContext ctx) throws Exception {

        ChannelId client = APPWSChannel.channelsText.remove(ctx.channel().id().asLongText());
        APPWSChannel.channels.remove(client);
        super.channelUnregistered(ctx);

        if (ValueUtils.isNull(client)) {
            return;
        }
        String clientId = getClientIdString(ctx);
        Map<String, Object> dataJson = new HashMap<String, Object>();
        dataJson.put("cmd", "ws@quit@destroy");
        dataJson.put("router", getWebSocketConstant().getRouter());
        dataJson.put("seq", -1);
//        dataJson.put("traceId", getSnowflakeIdWorker().nextId());
        dataJson.put("clientId", clientId);

    }

}
